package cz.muni.fi.pa165.creatures.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

/**
 * This class represent the type of a {@link Creature}.
 *
 * @author xmiklos1
 */
@Entity
@Table(name = "CREATURE_TYPE", uniqueConstraints = {
    @UniqueConstraint(columnNames = "NAME")
})
public class CreatureType implements Serializable {

    /**
     * Id of the creature type
     */
    private Long id;
    
    /**
     * Name of the creature type.
     */
    private String name;
    
    /**
     * Description of the creature type.
     */
    private String description;
    
    /**
     * Maximum hit points of the creature of some type. More hit points a 
     * creature has, more difficult is to kill it by some {@link Weapon}. 
     * 
     * Effectiveness of some {@link Weapon} against particular {@link CreatureType} 
     * is described by {@link CreatureTypeWeaponEffectivity#effectivity}.
     */
    private Integer maxHitPoints;
    
    /**
     * Typical number of the creatures of some type in the group while moving.
     */
    private Integer creaturesInGroup;
    private Set<CreatureTypeWeaponEffectivity> creatureTypeWeaponEffectivities 
            = new HashSet<CreatureTypeWeaponEffectivity>(0);
 
    /**
     * Creatures of this creature type.
     */
    private List<Creature> creatures = new ArrayList<Creature>();

//    Should be replaced by some enumeration
//    
//    /**
//     * Defense strategies which a creature of some creature type uses the most
//     */
//    private List<DefenseStrategy> defenseStrategies = new ArrayList<DefenseStrategy>();
    
    /**
     * Default constructor
     */
    public CreatureType() {
    }

    /**
     * Both parameters can not be null.
     * 
     * @param name name of the creature type (e.g. alien) 
     * @param maxHitPoints default maximum hit points some particular creature type has
     */
    public CreatureType(String name, Integer maxHitPoints) {
        this.name = name;
        this.maxHitPoints = maxHitPoints;
    }

    /**
     * 
     * @param name name of the creature type (e.g. alien)
     * @param maxHitPoints default maximum hit poinst some particular creature type has
     * @param creatureTypeWeaponEffectivities 
     */
    public CreatureType(String name, Integer maxHitPoints,
            Set<CreatureTypeWeaponEffectivity> creatureTypeWeaponEffectivities) {
        this.name = name;
        this.maxHitPoints = maxHitPoints;
        this.creatureTypeWeaponEffectivities = creatureTypeWeaponEffectivities;
    }

    /**
     * 
     * @return id of the creature type 
     */
    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "ID", nullable = false)
    public Long getId() {
        return this.id;
    }

    /**
     * Sets id of a creature
     * 
     * @param id id of a creature
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * Get a name of a creature.
     * 
     * @return name of a creature type
     */
    @Column(name = "NAME", nullable = false, length = 255)
    public String getName() {
        return this.name;
    }

    /**
     * Sets a name of a creature.
     * 
     * @param name name of a creature, may not be longer 
     * than 255 characters and has to be present
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Gets a description of a creature.
     * 
     * @return description of a creature
     */
    @Column(name = "DESCRIPTION", length = 512)
    public String getDescription() {
        return this.description;
    }

    /**
     * Sets a description of a creature. 
     * Typically arbitrary text of the length at 
     * most 512 characters.
     * 
     * @param description description of the creature
     */
    public void setDescription(String description) {
        this.description = description;
    }

    /**
     * Gets maximum hit points of the creature.
     * 
     * @return maximum hit points of the creature
     */
    @Column(name = "MAX_HIT_POINTS", nullable = false)
    public Integer getMaxHitPoints() {
        return this.maxHitPoints;
    }

    /**
     * Sets maximum hit points of a creature.
     * 
     * Tip - imagine an average human has 100 hit points. 
     * If you set health to 200 hit points, that creature 
     * should be twice as hard to kill as human.
     * 
     * @param maxHitPoints maximum hit points of the creature
     */
    public void setMaxHitPoints(Integer maxHitPoints) {
        this.maxHitPoints = maxHitPoints;
    }

    /**
     * Gets typical number of creatures while moving in a group.
     * 
     * @return typical number of creatures while moving in a group
     */
    @Column(name = "CREATURES_IN_GROUP")
    public Integer getCreaturesInGroup() {
        return this.creaturesInGroup;
    }

    /**
     * Sets typical number of creatures while moving in a group.
     * 
     * @param creaturesInGroup typical number of creatures while moving in a group
     */
    public void setCreaturesInGroup(Integer creaturesInGroup) {
        this.creaturesInGroup = creaturesInGroup;
    }

    /**
     * 
     * @return 
     */
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.creaturetype")
    public Set<CreatureTypeWeaponEffectivity> getCreatureTypeWeaponEffectivities() {
        return this.creatureTypeWeaponEffectivities;
    }

    /**
     * 
     * @param creatureTypeWeaponEffectivities 
     */
    public void setCreatureTypeWeaponEffectivities(Set<CreatureTypeWeaponEffectivity> creatureTypeWeaponEffectivities) {
        this.creatureTypeWeaponEffectivities = creatureTypeWeaponEffectivities;
    }
    
   /**
    * Gets list of creatures of this creature type.
    *
    * @return list of creatures of this creature type
    */
    @OneToMany(mappedBy = "creatureType")
    public List<Creature> getCreatures() {
        return creatures;
    }

   /**
    * Sets list of creatures of this creature type.
    *
    * @param creatures list of creatures of this creature type
    */
    public void setCreatures(List<Creature> creatures) {
        this.creatures = creatures;
    }

//   Can be actually deleted since the having of the fifth enitity is not permitted.
//    
//   Should be replaced by enum type as discussed 
//    
//    
//   /**
//    * Gets list of defense strategies of this creature type.
//    *
//    * @return list of defense strategies of this creature type
//    */
//    @ManyToMany(cascade = {CascadeType.ALL})
//    @JoinTable(name = "CREATURE_DEFENSE_STRATEGY", 
//            joinColumns = {@JoinColumn(name = "CREATURE_TYPE_ID")},
//            inverseJoinColumns = {@JoinColumn(name = "DEFENSE_STRATEGY_ID")})
//    public List<DefenseStrategy> getDefenseStrategies() {
//        return defenseStrategies;
//    }

//   /**
//    * Sets list of defense strategies of this creature type.
//    *
//    * @param defenseStrategies list of defense strategies of this creature type
//    */
//    public void setDefenseStrategies(List<DefenseStrategy> defenseStrategies) {
//        this.defenseStrategies = defenseStrategies;
//    }    
    
    @Override
    public String toString() {
        return "CreatureType{" + "id=" + id + ", name=" + name + ", description="
                + description + ", maxHitPoints=" + maxHitPoints + ", creaturesInGroup="
                + creaturesInGroup + ", creatureTypeWeaponEffectivities="
                + creatureTypeWeaponEffectivities + '}';
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 59 * hash + (this.id != null ? this.id.hashCode() : 0);
        hash = 59 * hash + (this.name != null ? this.name.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final CreatureType other = (CreatureType) obj;
        if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) {
            return false;
        }
        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
            return false;
        }
        return true;
    }

}
